package service

import (
	cacheOp "bgs/cache/op"
	sqlc "bgs/db/sqlc"
	"bgs/grpc/gen/proxy2/inventory"
	"bgs/service/model"
	"bgs/util"
	"context"
	"database/sql"
	"encoding/json"
	"fmt"
	"time"
)

//
var (
	ActivitiesAsFeature = []string{
		string(sqlc.EmActivityTypeCheckIn1),
	}
	activitiesCanEndorse = []string{
		string(sqlc.EmActivityTypeGroupBuying1),
	}
)

// CheckIsAsFeature 活动类型是否作为功能特性(签到)
func CheckIsAsFeature(activityType string) bool {
	return util.StringContains(ActivitiesAsFeature, activityType)
}

// CheckCanEndorse 活动类型是否能代言(暗含品牌唯一)
func CheckCanEndorse(activityType string) bool {
	return util.StringContains(activitiesCanEndorse, activityType)
}

// CheckAvailableActivityAsFeatureForBrand 获取所有作为功能的活动类型在品牌侧是否开启
func (s *ActivityServiceImp) CheckAvailableActivityAsFeatureForBrand(ctx context.Context, q *sqlc.Queries, brandID int64) (checkResults []*model.ActivityTypeAsFeatureCheckAvailable, err error) {
	for _, activityType := range ActivitiesAsFeature {
		var isAvailable bool
		var sqlErr error
		switch sqlc.EmActivityType(activityType) {
		case sqlc.EmActivityTypeCheckIn1:
			isAvailable, sqlErr = q.CheckAvailableCheckIn1Activity(ctx, brandID)
			if sqlErr != nil {
				if sql.ErrNoRows == sqlErr {
					// 找不到记录
					isAvailable = false
				} else {
					err = sqlErr
					return
				}
			}
		}
		checkResults = append(checkResults, &model.ActivityTypeAsFeatureCheckAvailable{
			ActivityType: activityType,
			IsAvailable:  isAvailable,
		})
	}
	return
}

// ClearCacheOwn 清空当前活动缓存
func (s *ActivityServiceImp) ClearCacheOwn(activityID int64) {
	s.cacher.UnsetActivity(activityID)
}

// GetCacheOwn 获取当前活动缓存
func (s *ActivityServiceImp) GetCacheOwn(activityID int64) *cacheOp.ActivityCachedValue {
	return s.cacher.FetchActivity(activityID)
}

// CheckActivityBeforeUserInteracted
func (s *ActivityServiceImp) CheckActivityBeforeUserInteracted(activityPtr *sqlc.Activity) (err error) {
	if activityPtr.ProcessStatus != string(sqlc.EmActivityProcessStatusProcessing) {
		err = fmt.Errorf("活动不可用")
		return
	}
	if time.Now().Before(activityPtr.Begined) {
		err = fmt.Errorf("活动未开始")
		return
	}
	if time.Now().After(activityPtr.Ended) {
		err = fmt.Errorf("活动已结束")
		return
	}
	return
}

// ClearCacheCascade 清空当前活动及派生的活动缓存
func (s *ActivityServiceImp) ClearCacheCascade(rawCtx context.Context, activityID int64) {
	go func(reqID interface{}) {
		ctx := util.NewContextWithReqID(reqID)
		slog := util.NewContextLogger(ctx, s.slog)
		defer util.Catch(ctx, slog)

		activityIDs, err := s.dao.Q.ListActivityIDsCascadeByActivityID(ctx, activityID)
		if err != nil {
			slog.Infof("ListActivityIDsCascadeByActivityID err:%s", err.Error())
			s.ClearCacheOwn(activityID)
			return
		}
		for _, id := range activityIDs {
			s.cacher.UnsetActivity(id)
		}
	}(util.GetReqID(rawCtx))
}

func mapToActivitySkuWithProductInfo(list []sqlc.ListNormalActivitySkuWithProductInfosByActivityIDRow, f func(sqlc.ListNormalActivitySkuWithProductInfosByActivityIDRow) *model.ActivitySkuWithProductOrProductSpecInfo) []*model.ActivitySkuWithProductOrProductSpecInfo {
	r := make([]*model.ActivitySkuWithProductOrProductSpecInfo, len(list))
	for i, item := range list {
		r[i] = f(item)
	}
	return r
}

func transActivitySkuWithProductInfoTo(o sqlc.ListNormalActivitySkuWithProductInfosByActivityIDRow) *model.ActivitySkuWithProductOrProductSpecInfo {
	return &model.ActivitySkuWithProductOrProductSpecInfo{
		ID:             o.ID,
		ProductID:      o.ProductID,
		ProductSpecID:  o.ProductSpecID.Int64,
		Price:          o.Price,
		Maximum:        o.Maximum,
		FreezeQuantity: o.FreezeQuantity,
		SoldedQuantity: o.SoldedQuantity,
		StorageKey:     o.StorageKey,
		// product_spec info
		SkuName: o.SkuName,
	}
}

func mapToActivitySkuWithProductSpecInfo(list []sqlc.ListNormalActivitySkuWithProductSpecInfosByActivityIDRow, f func(sqlc.ListNormalActivitySkuWithProductSpecInfosByActivityIDRow) *model.ActivitySkuWithProductOrProductSpecInfo) []*model.ActivitySkuWithProductOrProductSpecInfo {
	r := make([]*model.ActivitySkuWithProductOrProductSpecInfo, len(list))
	for i, item := range list {
		r[i] = f(item)
	}
	return r
}

func transActivitySkuWithProductSpecInfoTo(o sqlc.ListNormalActivitySkuWithProductSpecInfosByActivityIDRow) *model.ActivitySkuWithProductOrProductSpecInfo {
	return &model.ActivitySkuWithProductOrProductSpecInfo{
		ID:             o.ID,
		ProductID:      o.ProductID,
		ProductSpecID:  o.ProductSpecID.Int64,
		Price:          o.Price,
		Maximum:        o.Maximum,
		FreezeQuantity: o.FreezeQuantity,
		SoldedQuantity: o.SoldedQuantity,
		StorageKey:     o.StorageKey,
		// product_spec info
		SkuName: o.SkuName,
	}
}

// PublishActivityParam 发布活动参数
type PublishActivityParam struct {
	ActivityPtr *sqlc.Activity
	UserID      int64
	WarehouseID int64
	Skus        []*model.ActivitySkuWithProductOrProductSpecInfo
}

// PublishActivity 发布活动
func (s *ActivityServiceImp) PublishActivity(ctx context.Context, q *sqlc.Queries, p PublishActivityParam) (processStatus string, err error) {
	slog := util.NewContextLogger(ctx, s.slog)

	processStatus = string(sqlc.EmActivityProcessStatusProcessing)

	param := sqlc.UpdateActivityProcessStatusParams{
		ID:            p.ActivityPtr.ID,
		ProcessStatus: processStatus,
		Published:     sqlc.NTimeFromTime(time.Now()),
	}

	if p.ActivityPtr.Owner == string(sqlc.EmActivityOwnerSpokesman) {
		slog.Infof("1 代言人活动无须创建仓库侧冻结单(for b) ok")
	} else {
		// 将设置了库存的sku 过滤传入
		var skusWithMaxium []*model.ActivitySkuWithProductOrProductSpecInfo
		util.Each(util.ToGenericSlice(p.Skus), func(v interface{}) {
			val := v.(*model.ActivitySkuWithProductOrProductSpecInfo)
			if val.Maximum > 0 {
				skusWithMaxium = append(skusWithMaxium, val)
			}
		})

		if len(skusWithMaxium) > 0 {
			var inventoryFreezeOrderNumber string
			inventoryFreezeOrderNumber, err = s.CreateInventoryFreezeOrderForB(ctx, p.ActivityPtr, skusWithMaxium, p.WarehouseID, p.UserID)
			if err != nil {
				return
			}
			slog.Infof("1 非代言人活动创建仓库侧冻结单(for b/s) 锁定活动库存 ok")
			param.InventoryFreezeNo = sqlc.NSFromString(inventoryFreezeOrderNumber)
		} else {
			slog.Infof("1 非代言人活动没有需要锁定库存的sku ok")
		}
	}
	_, err = q.UpdateActivityProcessStatus(ctx, param)
	if err != nil {
		return
	}
	slog.Infof("2 发布活动 ok")
	return
}

// UpdateActivityTypeInfoSinceActivityPublished 在发布活动成功后更新活动类型信息
func (s *ActivityServiceImp) UpdateActivityTypeInfoSinceActivityPublished(ctx context.Context, q *sqlc.Queries, activityPtr *sqlc.Activity) (err error) {
	slog := util.NewContextLogger(ctx, s.slog)

	if model.CheckNeedTypeInfo(activityPtr.Type) {
		typeInfoPtr := &model.ActivityTypeInfo{}
		json.Unmarshal(activityPtr.TypeInfo.RawMessage, typeInfoPtr)
		activityType := sqlc.EmActivityType(activityPtr.Type)
		switch activityType {
		case sqlc.EmActivityTypeLottery1:
			distributions, derr := GenLottery1PrizeDistribution(ctx, typeInfoPtr.Lottery1Ptr.PrizeRates, typeInfoPtr.Lottery1Ptr.Cardinality)
			if derr != nil {
				err = derr
				return
			}
			typeInfoPtr.Lottery1Ptr.Distributions = distributions
			typeInfoBytes, marshalErr := json.Marshal(typeInfoPtr)
			if marshalErr != nil {
				err = marshalErr
				return
			}
			err = q.UpdateActivityOfBTypeInfo(ctx, sqlc.UpdateActivityOfBTypeInfoParams{
				ID:       activityPtr.ID,
				TypeInfo: sqlc.NRawMessageFromRawMessage(json.RawMessage(typeInfoBytes)),
			})
			if err != nil {
				return
			}
			slog.Infof("1 在发布活动成功后更新[抽奖1]类型信息 ok")
		}

	}

	return
}

// PauseResumeActivityParam 停用启用活动活动参数
type PauseResumeActivityParam struct {
	ActivityPtr *sqlc.Activity
}

// PauseActivity 停用活动
func (s *ActivityServiceImp) PauseActivity(ctx context.Context, q *sqlc.Queries, p PauseResumeActivityParam) (processStatus string, err error) {
	slog := util.NewContextLogger(ctx, s.slog)

	processStatus = string(sqlc.EmActivityProcessStatusPaused)
	_, err = q.UpdateActivityProcessStatusCascade(ctx, sqlc.UpdateActivityProcessStatusCascadeParams{
		ID:            p.ActivityPtr.ID,
		ProcessStatus: processStatus,
		Paused:        sqlc.NTimeFromTime(time.Now()),
	})
	if err != nil {
		return
	}

	slog.Infof("停用活动 ok")
	return
}

// ResumeActivity 恢复活动
func (s *ActivityServiceImp) ResumeActivity(ctx context.Context, q *sqlc.Queries, p PauseResumeActivityParam) (processStatus string, err error) {
	slog := util.NewContextLogger(ctx, s.slog)

	processStatus = string(sqlc.EmActivityProcessStatusDraft)
	if p.ActivityPtr.Published.Valid {
		processStatus = string(sqlc.EmActivityProcessStatusProcessing)
	}

	_, err = q.UpdateActivityProcessStatusCascade(ctx, sqlc.UpdateActivityProcessStatusCascadeParams{
		ID:            p.ActivityPtr.ID,
		ProcessStatus: processStatus,
		Resumed:       sqlc.NTimeFromTime(time.Now()),
	})
	if err != nil {
		return
	}

	slog.Infof("恢复活动 ok")
	return
}

// FetchNormalProducts activity products
func (s *ActivityServiceImp) FetchNormalProducts(ctx context.Context, q *sqlc.Queries, activityID int64) (normalActivityProductWithProductInfos []sqlc.ListNormalActivityProductWithProductInfosByActivityIDRow, err error) {
	normalActivityProductWithProductInfos, err = q.ListNormalActivityProductWithProductInfosByActivityID(ctx, activityID)
	if err != nil {
		return
	}
	return
}

// FetchNormalProductAndSkus activity products with skus
func (s *ActivityServiceImp) FetchNormalProductAndSkus(ctx context.Context, q *sqlc.Queries, activityID int64) (normalActivityProductWithProductInfos []sqlc.ListNormalActivityProductWithProductInfosByActivityIDRow, normalActivitySkus []*model.ActivitySkuWithProductOrProductSpecInfo, err error) {

	normalActivityProductWithProductInfos, err = q.ListNormalActivityProductWithProductInfosByActivityID(ctx, activityID)
	if err != nil {
		return
	}

	normalActivitySkus, err = s.FetchNormalSkus(ctx, q, activityID)
	if err != nil {
		return
	}

	return
}

// FetchNormalSkus activity skus
func (s *ActivityServiceImp) FetchNormalSkus(ctx context.Context, q *sqlc.Queries, activityID int64) (normalActivitySkus []*model.ActivitySkuWithProductOrProductSpecInfo, err error) {

	normalActivitySkuWithProductInfos, err := q.ListNormalActivitySkuWithProductInfosByActivityID(ctx, activityID)
	if err != nil {
		return
	}
	if len(normalActivitySkuWithProductInfos) > 0 {
		normalActivitySkus = append(normalActivitySkus, mapToActivitySkuWithProductInfo(normalActivitySkuWithProductInfos, transActivitySkuWithProductInfoTo)...)
	}

	normalActivitySkuWithProductSpecInfos, err := q.ListNormalActivitySkuWithProductSpecInfosByActivityID(ctx, activityID)
	if err != nil {
		return
	}
	if len(normalActivitySkuWithProductSpecInfos) > 0 {
		normalActivitySkus = append(normalActivitySkus, mapToActivitySkuWithProductSpecInfo(normalActivitySkuWithProductSpecInfos, transActivitySkuWithProductSpecInfoTo)...)
	}

	return
}

type FetchProductOrSkusWithRealtimeStockParam struct {
	ActivityID int64
	ParentID   int64
	Owner      sqlc.EmActivityOwner
}

// FetchNormalProductAndSkusWithRealtimeStock activity products with skus with realtime stock
func (s *ActivityServiceImp) FetchNormalProductAndSkusWithRealtimeStock(ctx context.Context, q *sqlc.Queries, ptr *FetchProductOrSkusWithRealtimeStockParam) (normalActivityProductWithProductInfos []sqlc.ListNormalActivityProductWithProductInfosByActivityIDRow, normalActivitySkus []*model.ActivitySkuWithProductOrProductSpecInfo, err error) {

	normalActivityProductWithProductInfos, err = q.ListNormalActivityProductWithProductInfosByActivityID(ctx, ptr.ActivityID)
	if err != nil {
		return
	}

	normalActivitySkus, err = s.FetchNormalSkusWithRealtimeStock(ctx, q, ptr)
	if err != nil {
		return
	}

	return
}

// FetchNormalSkusWithRealtimeStock activity skus with skus with realtime stock
func (s *ActivityServiceImp) FetchNormalSkusWithRealtimeStock(ctx context.Context, q *sqlc.Queries, ptr *FetchProductOrSkusWithRealtimeStockParam) (normalActivitySkus []*model.ActivitySkuWithProductOrProductSpecInfo, err error) {

	// 目前代言活动的sku信息和品牌活动的sku信息共用,所以代言活动获取sku可以用自身的parent_activity_id
	activityID := util.Int64IfThenElse(ptr.Owner == sqlc.EmActivityOwnerSpokesman, ptr.ParentID, ptr.ActivityID)

	normalActivitySkuWithProductInfos, err := q.ListNormalActivitySkuWithProductInfosByActivityID(ctx, activityID)
	if err != nil {
		return
	}
	if len(normalActivitySkuWithProductInfos) > 0 {
		normalActivitySkus = append(normalActivitySkus, mapToActivitySkuWithProductInfo(normalActivitySkuWithProductInfos, transActivitySkuWithProductInfoTo)...)
	}

	normalActivitySkuWithProductSpecInfos, err := q.ListNormalActivitySkuWithProductSpecInfosByActivityID(ctx, activityID)
	if err != nil {
		return
	}
	if len(normalActivitySkuWithProductSpecInfos) > 0 {
		normalActivitySkus = append(normalActivitySkus, mapToActivitySkuWithProductSpecInfo(normalActivitySkuWithProductSpecInfos, transActivitySkuWithProductSpecInfoTo)...)
	}

	return
}

// FetchConsignmentAndConsignmentBonusSettings 获取活动代言佣金设置
func (s *ActivityServiceImp) FetchConsignmentAndConsignmentBonusSettings(ctx context.Context, q *sqlc.Queries, activityID int64) (activityConsignment sqlc.ActivityConsignment, normalActivityConsignmentBonusSettings []sqlc.ActivityConsignmentBonusSetting, err error) {
	activityConsignment, err = q.GetActivityConsignmentByActivityID(ctx, activityID)
	if err != nil {
		return
	}

	normalActivityConsignmentBonusSettings, err = q.ListNormalActivityConsignmentBonusSettingsByActivityID(ctx, activityID)
	if err != nil {
		return
	}
	return
}

// CreateInventoryFreezeOrderForB 创建仓库侧冻结单(for b) 锁定活动库存
func (s *ActivityServiceImp) CreateInventoryFreezeOrderForB(ctx context.Context, activityPtr *sqlc.Activity, skus []*model.ActivitySkuWithProductOrProductSpecInfo, warehouseID int64, operator int64) (inventoryFreezeOrderNumber string, err error) {
	slog := util.NewContextLogger(ctx, s.slog)
	// 拿取sg_id、活动仓库ckid,活动库存的storageKey
	brandID := activityPtr.BrandID
	cBrand := s.cacher.FetchBrand(brandID)
	if cBrand == nil {
		err = fmt.Errorf("not found brand of brandID:%d", brandID)
		return
	}
	sgID := cBrand.SgID

	cWarehouse := s.cacher.FetchWarehouse(warehouseID)
	if cWarehouse == nil {
		err = fmt.Errorf("not found warehouse :%d", warehouseID)
		return
	}
	ckID := cWarehouse.CkID

	var details []*inventory.CreateFreezeOrderDetail
	for _, item := range skus {
		details = append(details, &inventory.CreateFreezeOrderDetail{
			Id:          ckID, //远端仓库id
			StorageType: inventory.StorageType_PROD,
			StorageKey:  item.StorageKey,
			Quantity:    int64(item.Maximum),
			StorageName: item.SkuName, // product_name+spec_name
			WeightRef:   0,
		})
		slog.Infof("detail item=>%+v", item)
	}

	orderKey := util.PaddingLeftZero(operator, 10)
	requestNum := util.UUID()
	req := &inventory.CreateFreezeOrderRequest{
		SgId: sgID,
		// RequestNum: strconv.FormatInt(activityPtr.ID, 10),
		RequestNum: requestNum,
		Source:     inventory.SourceEnum_XT_ACTIVITY,
		OrderKey:   orderKey,
		ExpireTime: util.GoTimeToString(activityPtr.Ended),
		Details:    details,
	}

	ctxWithTimout, cancel := s.proxy.NewContextWith(ctx)
	defer cancel()
	slog.Infof("------CreateFreezeOrder-----")
	reply, err := s.proxy.InventoryCl.CreateFreezeOrder(ctxWithTimout, req)
	if err != nil {
		errReply := s.proxy.GetInventoryErrorInfo(err)
		if errReply != nil {
			err = fmt.Errorf("仓库侧创建活动冻结单错误:%s", errReply.ErrorMessage)
			slog.Infof("InventoryCl.CreateFreezeOrder of activity err====>type:%s,code:%s,msg:%s", errReply.ErrorType, errReply.ErrorCode, errReply.ErrorMessage)
		}
		return
	}

	inventoryFreezeOrderNumber = reply.OrderNumber
	slog.Infof("1 创建仓库侧冻结单 ok")
	return
}

// UpdateInventoryFreezeOrderForB 更新仓库侧冻结单(for b) 锁定活动库存
func (s *ActivityServiceImp) UpdateInventoryFreezeOrderForB(ctx context.Context, activityPtr *sqlc.Activity, skus []*model.ActivitySkuWithProductOrProductSpecInfo, warehouseID int64) (err error) {
	slog := util.NewContextLogger(ctx, s.slog)
	// 拿取sg_id、活动仓库ckid,活动库存的storageKey

	brandID := activityPtr.BrandID
	cBrand := s.cacher.FetchBrand(brandID)
	if cBrand == nil {
		err = fmt.Errorf("not found brand of brandID:%d", brandID)
		return
	}
	sgID := cBrand.SgID

	cWarehouse := s.cacher.FetchWarehouse(warehouseID)
	if cWarehouse == nil {
		err = fmt.Errorf("not found warehouse :%d", warehouseID)
		return
	}
	ckID := cWarehouse.CkID

	var details []*inventory.CreateFreezeOrderDetail
	for _, item := range skus {

		details = append(details, &inventory.CreateFreezeOrderDetail{
			Id:          ckID, //远端仓库id
			StorageType: inventory.StorageType_PROD,
			StorageKey:  item.StorageKey,
			Quantity:    int64(item.Maximum),
			StorageName: item.SkuName, // product_name+spec_name
			WeightRef:   0,
		})
	}

	req := &inventory.UpdateFreezeOrderRequest{
		SgId:        sgID,
		OrderNumber: activityPtr.InventoryFreezeNo.String,
		// RequestNum:  strconv.FormatInt(activityPtr.ID, 10),
		Source:     inventory.SourceEnum_XT_ACTIVITY,
		ExpireTime: util.GoTimeToString(activityPtr.Ended),
		Details:    details,
	}

	ctxWithTimout, cancel := s.proxy.NewContextWith(ctx)
	defer cancel()

	_, err = s.proxy.InventoryCl.UpdateFreezeOrder(ctxWithTimout, req)
	if err != nil {
		errReply := s.proxy.GetInventoryErrorInfo(err)
		if errReply != nil {
			err = fmt.Errorf("仓库侧更新活动冻结单错误:%s", errReply.ErrorMessage)
			slog.Infof("InventoryCl.UpdateFreezeOrder of activity err====>type:%s,code:%s,msg:%s", errReply.ErrorType, errReply.ErrorCode, errReply.ErrorMessage)
		}
		return
	}

	slog.Infof("1 更新仓库侧冻结单 ok")
	return
}
